﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Controls;

using PlanningGIS.ArcGIS;
using PlanningGIS.Util.Log;
using PlanningGIS.Engine.Controls;


namespace PlanningGIS.DataCheck.model
{
    /// <summary>
    /// 框架要素检查
    /// </summary>
    public class FrameCheck : Check
    {
        public IActiveView pAV;//测试用

        /// <summary>
        /// 检查
        /// </summary>
        /// <param name="errType">错误类型</param>
        /// <param name="pFeatLyrSmall">中小比例尺图层</param>
        /// <param name="pFeatLyrLarge">大比例尺图层</param>
        /// <param name="pGeometry">检查范围，若是null，则为全图范围</param>
        /// <param name="buffer">中小比例尺要素所开缓冲大小，若是0，则不开缓冲</param>
        /// <param name="fieldNameSmall">中小比例尺数据的属性字段名，若是""（空字符串），则不考虑属性对应关系</param>
        /// <param name="fieldNameLarge">大比例尺数据的属性字段名，若是""（空字符串），则不考虑属性对应关系</param>
        /// <param name="upper">比例上限，若是0，则无需进行比例校验</param>
        /// <param name="lower">比例下限，若是0，则无需进行比例校验</param>
        /// <param name="limit">大比例尺阈值，表示大比例尺要素与小比例尺要素图形（或者缓冲区）相交部分与自己的比例大于该值才记录到总和；正常情况下该值在0和1之间，0表示搜到的要素都记录到总和，1表示全部落入才记录到总和；如果是-1，表示无需进行比例校验</param>
        /// <returns></returns>
        public int Check(string errType, IFeatureLayer pFeatLyrSmall, IFeatureLayer pFeatLyrLarge, IGeometry pGeometry, 
            double buffer, string fieldNameSmall, string fieldNameLarge, double upper, double lower,double limit)
        {
            IFeatureClass pFeatClsSmall = pFeatLyrSmall.FeatureClass;
            IFeatureClass pFeatClsLarge = pFeatLyrLarge.FeatureClass;

            #region 条件判断
            if (pFeatClsSmall.FeatureType != pFeatClsLarge.FeatureType)
            {
               LogHelper.WriteErrLog("中小比例尺数据与大比例尺数据的要素类型不一致，无法进行检查！");
                return -1;
            }

            if (pFeatClsSmall.ShapeType != pFeatClsLarge.ShapeType)
            {
               LogHelper.WriteErrLog("中小比例尺数据与大比例尺数据的几何类型不一致，无法进行检查！");
                return -1;
            }

            if (buffer < 0)
            {
               LogHelper.WriteErrLog("缓冲值必须为非负数！");
                return -1;
            }

            #region 字段条件
            int idxSmall, idxLarge;
            IField pFieldSmall = null, pFieldLarge = null;

            if (fieldNameSmall.Trim() == "" || fieldNameLarge.Trim() == "")
            {
                idxSmall = -1;
                idxLarge = -1;
            }
            else
            {
                idxSmall = pFeatClsSmall.FindField(fieldNameSmall);
                if (idxSmall == -1)
                {
                   LogHelper.WriteErrLog("[" + pFeatLyrSmall.Name + "]图层中未找到[" + fieldNameSmall + "]字段，无法进行检查！");
                    return -1;
                }

                idxLarge = pFeatClsLarge.FindField(fieldNameSmall);
                if (idxLarge == -1)
                {
                   LogHelper.WriteErrLog("[" + pFeatLyrLarge.Name + "]图层中未找到[" + fieldNameLarge + "]字段，无法进行检查！");
                    return -1;
                }

                pFieldSmall = pFeatClsSmall.Fields.get_Field(idxSmall);
                pFieldLarge = pFeatClsLarge.Fields.get_Field(idxLarge);

                if (pFieldSmall.Type != pFieldLarge.Type)
                {
                   LogHelper.WriteErrLog("两个比例尺数据所设置的字段类型不一致，无法进行检查！");
                    return -1;
                }

                if ((int)pFieldLarge.Type > 4)
                {
                   LogHelper.WriteErrLog("所选字段必须为数值或者字符型！");
                    return -1;
                }
            }
            #endregion

            #region 比例条件
            if ((upper == 0) ^ (lower == 0))    //有一个为0
            {
               LogHelper.WriteErrLog("上限和下限必须同时为0，或者同时不为0");
                return -1;
            }
            else if (upper != 0 && lower != 0)  //同时不为0
            {
                if (limit < 0 && limit > 1)
                {
                   LogHelper.WriteErrLog("大比例尺阈值必须在0与1之间！");
                    return -1;
                }

                if (upper < 1)
                {
                   LogHelper.WriteErrLog("上限必须大于或等于1！");
                    return -1;
                }

                if (lower < 0 || lower > 1)
                {
                   LogHelper.WriteErrLog("下限必须是小于或等于1的正数！");
                    return -1;
                }
            }
            else                                //同时为0
            {
                limit = -1;
            }

            //对于点要素，无需进行比例校验
            if (pFeatClsLarge.ShapeType == esriGeometryType.esriGeometryPoint)
            {
                limit = -1;
                upper = lower = 0;
            }
            #endregion

            #endregion

            ISpatialReferenceFactory pSpatRefFact = new SpatialReferenceEnvironment();
            IProjectedCoordinateSystem projectedCoordinateSystem2 = pSpatRefFact.CreateProjectedCoordinateSystem((int)esriSRProjCS4Type.esriSRProjCS_Xian1980_GK_CM_117E); 
            
            ISpatialFilter pSpatialFileter = new SpatialFilterClass();
            if (pGeometry != null) pSpatialFileter.Geometry = pGeometry;

            pSpatialFileter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;
            pSpatialFileter.GeometryField = pFeatClsSmall.ShapeFieldName;

            showFakePro(true);
            LogHelper.WriteLog("正在计算需要检查的要素数目...");

            int max = FeatureClassHelper.GetFeatureCountFromLayer(pFeatLyrSmall, pSpatialFileter);// pFeatClsSmall.FeatureCount(pSpatialFileter);

            LogHelper.WriteLog("计算完毕。共有" + max + "个要素参与检查。");
            showFakePro(false);

            if (max == 0)
            {
                LogHelper.WriteLog("没有满足检查要求的要素存在。");
                return -1;
            }

            setProcessBarMax(max);

            IFeatureCursor pFeatCur;
            pFeatCur = pFeatLyrSmall.Search(pSpatialFileter, false);

            IFeature pFeatureSmall;

            int count = 0;
            int errCount = 0;

            while ((pFeatureSmall = pFeatCur.NextFeature()) != null)
            {
                if (IsStop()) break;
                
                count++;
                setProcessBarCurrent(count);
    

                IGeometry pShapeCopySmall = pFeatureSmall.ShapeCopy;
                if (pShapeCopySmall == null || pShapeCopySmall.IsEmpty) continue;

                double smallOne = 0;
                double largeAll = 0;

                #region 获取中小比例尺要素的长度或面积
                if (limit != -1)
                {
                    switch (pShapeCopySmall.GeometryType)
                    {
                        case esriGeometryType.esriGeometryPolyline:
                            IPolyline pPolyline = pFeatureSmall.ShapeCopy as IPolyline;
                            pPolyline.Project(projectedCoordinateSystem2);
                            smallOne = pPolyline.Length;
                            break;

                        case esriGeometryType.esriGeometryPolygon:
                            IArea pArea = pFeatureSmall.ShapeCopy as IArea;
                            (pArea as IPolygon).Project(projectedCoordinateSystem2);
                            smallOne = pArea.Area;
                            break;

                        default:
                            break;
                    }
                }
                #endregion

                IGeometry pBuffer = GeometryHelper.GetBuffer(pShapeCopySmall, buffer);

                //-----------------------------------------------调试用
                //DisplayUtility.DrawPolygonXOR(pBuffer as IPolygon, pAV);
                //----------------------------------------------------

                #region 设置SQL语句
                string sql = "";
                object value = DBNull.Value;
                if (idxLarge != -1)
                {
                    value = pFeatureSmall.get_Value(idxSmall);

                    //数值型
                    if ((int)pFieldSmall.Type < 4)
                    {
                        if (value == DBNull.Value)
                        {
                            sql = fieldNameLarge + " is null";
                        }
                        else
                        {
                            sql = fieldNameLarge + " = " + value;
                        }
                    }
                    //字符型
                    else
                    {
                        if (value == DBNull.Value || value == "0" || value == "" || value == " ")
                        {
                            sql = fieldNameLarge + " is null or " + fieldNameLarge + " = '0' or " +
                                fieldNameLarge + " =  '' or " + fieldNameLarge + " = ' '";
                            value = DBNull.Value;
                        }
                        else
                        {
                            sql = fieldNameLarge + " = '" + value + "'";
                        }
                    }
                }
                #endregion

                ISelectionSet pSelSetLarge = CursorHelper.GetSelset(pFeatLyrLarge as IFeatureSelection, pBuffer, sql,
                    esriSpatialRelEnum.esriSpatialRelIntersects);

                if (pSelSetLarge.Count == 0)
                {
                    RecordListHelper.AddRecordItem(errType, pFeatLyrSmall, pFeatLyrLarge, pFeatureSmall, null, pFeatureSmall.ShapeCopy, "缺失对应大比例尺数据");
                    errCount++;
                    continue;
                }

                IEnumIDs pIDs = pSelSetLarge.IDs;
                pIDs.Reset();
                int id;

                while ((id = pIDs.Next()) != -1)
                {
                    IFeature pFeatureLarge = pFeatClsLarge.GetFeature(id);
                    
                    #region 获取大比例尺的长度或面积和
                    if (limit != -1)
                    {
                        IGeometry pShapeCopyLarge = pFeatureLarge.ShapeCopy;

                        ITopologicalOperator2 pTop = pShapeCopyLarge as ITopologicalOperator2;
                        pTop.IsKnownSimple_2 = false;
                        pTop.Simplify();

                        switch (pShapeCopyLarge.GeometryType)
                        {
                            case esriGeometryType.esriGeometryPolyline:
                                //这里重新取ShapCopy而不是用pShapeCopyLarge是因为获取长度或者面积时需要做投影转换，而后面又要做拓扑关系运算，若两者空间参考不一致，将会报错
                                IPolyline pPolylineLarge = pFeatureLarge.ShapeCopy as IPolyline;
                                IPolyline pPolylineIntersect = pTop.Intersect(pBuffer, esriGeometryDimension.esriGeometry1Dimension) as IPolyline;
                                
                                if (pPolylineIntersect == null || pPolylineIntersect.IsEmpty) break;

                                pPolylineLarge.Project(projectedCoordinateSystem2);
                                pPolylineIntersect.Project(projectedCoordinateSystem2);

                                if (pPolylineIntersect.Length / pPolylineLarge.Length >= limit)
                                    largeAll = largeAll + pPolylineLarge.Length;

                                break;

                            case esriGeometryType.esriGeometryPolygon:
                                IArea pAreaLarge = pFeatureLarge.ShapeCopy as IArea;   
                                IPolygon pPolygonIntersect = pTop.Intersect(pBuffer, esriGeometryDimension.esriGeometry2Dimension) as IPolygon;

                                if (pPolygonIntersect == null || pPolygonIntersect.IsEmpty) break;

                                (pAreaLarge as IPolygon).Project(projectedCoordinateSystem2);

                                IArea pAreaIntersect = pPolygonIntersect as IArea;
                                (pAreaIntersect as IPolygon).Project(projectedCoordinateSystem2);

                                if (pAreaIntersect.Area / pAreaLarge.Area >= limit)
                                    largeAll = largeAll + pAreaLarge.Area;
                                    //largeAll = +Math.Sqrt(pAreaLarge.Area);

                                break;

                            default:
                                break;
                        }
                    }
                    #endregion

                    //如果属性为空，则放宽对属性的约束；否则误查太多
                    if (value != DBNull.Value)
                    {
                        string errField = CheckFeatureAttribute(pFeatureSmall, pFeatureLarge);
                        if (errField != "")
                        {
                            RecordListHelper.AddRecordItem(errType, pFeatLyrSmall, pFeatLyrLarge, pFeatureSmall, pFeatureLarge,
                                pFeatureLarge.ShapeCopy, "[" + errField + "]字段不匹配");
                            errCount++;
                        }
                    }

                }

                if (limit  != -1)
                {
                    double scale = largeAll / smallOne;
                    //scale = Math.Sqrt(scale);
                    if (scale < lower || scale > upper)
                    {
                        RecordListHelper.AddRecordItem(errType, pFeatLyrSmall, pFeatLyrLarge, pFeatureSmall, null, pFeatureSmall.ShapeCopy, "图形不匹配");
                        errCount++;
                    }
                }

                (pFeatLyrLarge as IFeatureSelection).Clear();
            }

            return errCount;
        }


        /// <summary>
        /// 两个要素的属性对比
        /// </summary>
        /// <param name="pFeatSmall">小比例尺要素</param>
        /// <param name="pFeatLarge">大比例尺要素</param>
        /// <returns>属性是否一致</returns>
        private string CheckFeatureAttribute(IFeature pFeatSmall, IFeature pFeatLarge)
        {
            for (int i = 0; i < pFeatSmall.Fields.FieldCount; i++)
            {
                IField pField = pFeatSmall.Fields.get_Field(i);
                if (pField.Type == esriFieldType.esriFieldTypeGeometry) continue;
                if (!pField.Editable) continue;//对不可编辑的字段（一般都是系统字段）跳过

                int j = pFeatLarge.Fields.FindField(pField.Name);
                if (j == -1) continue;

                if (pField.Type != pFeatLarge.Fields.get_Field(j).Type) return pField.Name;//类型不匹配，也不一致

                //都为空，则跳过
                if ((pFeatSmall.get_Value(i) == null || pFeatSmall.get_Value(i) == DBNull.Value) &&
                    (pFeatLarge.get_Value(j) == null || pFeatLarge.get_Value(j) == DBNull.Value)) continue;

                object value1 = pFeatSmall.get_Value(i);
                object value2 = pFeatLarge.get_Value(j);

                if (pField.Type == esriFieldType.esriFieldTypeString)
                {
                    //对于字符串型的字段，空(null)、空字符串("")以及"0"都表示一个意思
                    if (value1 == null || value1 == DBNull.Value || value1.ToString() == "0") value1 = "";
                    if (value2 == null || value2 == DBNull.Value || value2.ToString() == "0") value2 = "";
                }
                else
                {
                    //对于不是字符串型的字段，只要有一个为空，则返回false（如果两个同时为空，前面已经判断过，会直接跳过）
                    if (value1 == null || value1 == DBNull.Value) return pField.Name;
                    if (value2 == null || value2 == DBNull.Value) return pField.Name;
                }

                //对于浮点型的字段，考虑到精度问题
                if (pField.Type == esriFieldType.esriFieldTypeSingle || pField.Type == esriFieldType.esriFieldTypeDouble)
                {
                    double d1 = Double.Parse(value1.ToString());
                    double d2 = double.Parse(value2.ToString());
                    if (Math.Abs(d1 - d2) < 0.000001) continue;

                }

                //只要有一对不一致，则属性不一致
                if (!value1.Equals(value2)) return pField.Name;

            }
            return "";
        }
    }
}
